package com.niodata.dp.monitor.core.task;

import com.niodata.dp.collection.StringList;
import com.niodata.dp.core.usermgr.model.GroupInfo;
import com.niodata.dp.core.usermgr.model.GroupProfile;
import com.niodata.dp.core.usermgr.op.GroupInfoService;
import com.niodata.dp.monitor.common.entity.MailItem;
import com.niodata.dp.monitor.common.entity.Message;
import com.niodata.dp.monitor.common.util.DingDingMsgUtil;
import com.niodata.dp.monitor.common.util.MailSendUtil;
import com.niodata.dp.monitor.common.util.MailSource;
import com.niodata.dp.monitor.core.task.entity.AlertInfo;
import com.niodata.dp.monitor.core.task.entity.AlertInfo.AlertType;
import com.niodata.dp.monitor.core.task.entity.JobStatus;
import com.niodata.dp.monitor.core.task.entity.KeyCache;
import com.niodata.dp.monitor.core.task.entity.TaskAlertSetting;
import com.niodata.dp.monitor.core.task.entity.TaskInfo;
import com.niodata.dp.monitor.core.task.util.JsonUtil;
import com.niodata.dp.monitor.core.task.util.TemplateUtil;
import com.niodata.dp.monitor.sharelib.entity.task.TaskExecution;
import com.niodata.dp.monitor.sharelib.util.TimeUtil;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service("TaskMonitorAlert")
public class TaskMonitorAlert {

  public static final String ALERT_FAILED = "FAILED";
  public static final String ALERT_STARTED = "STARTED";
  public static final String ALERT_FINISHED = "FINISHED";
  public static final String ALERT_TIMEOUT = "TIMEOUT";
  public static final String ALERT_SUCCEEDED = "SUCCEEDED";
  public static final String ALERT_TITLE = "Dp任务状态监控";
  public static final String ALERT_NOT_FOUND = "NOT FOUND";
  private static final Logger logger = Logger.getLogger(TaskMonitorAlert.class);
  private static final String MAP_KEY_GROUP_NAME = "groupName";
  private static final String MAP_KEY_TASK_NAME = "taskName";
  private static final String MAP_KEY_MSG = "msg";
  private static final String MAP_KEY_EXEC_ID = "execId";
  private static final String MAP_KEY_STATUS = "status";
  private static final String MAP_KEY_START_TIME = "startTime";
  private static final String MAP_KEY_RUNNUNG_TIME = "runningTime";
  private static final String MAP_KEY_EXPECT_TIME = "expectTime";
  private static final String MAP_KEY_TIME = "time";
  private static final String MAP_KEY_FREQUENCY = "frequency";
  private final int START_JOB_CACHE_TTL = 10 * 60 * 1000;
  @Autowired
  @Qualifier("taskManagerImpl")
  private TaskExecutionManager taskManager;
  @Autowired
  @Qualifier("DpUserMgrGroup")
  private GroupInfoService groupInfoService;
  private KeyCache startJobCache = new KeyCache();
  @Value("${dp.url}")
  private String dpUrl;

  /**
   * check if alert needed .
   */
  public void sendAlertIfNeeded(TaskExecution execution) throws Exception {

    TaskInfo taskInfo = this.taskManager.getTaskByName(execution.getTaskName());

    if (null == taskInfo) {
      return;
    }

    TaskAlertSetting taskAlertSetting = taskInfo.getAlertSetting();
    if (null == taskAlertSetting) {
      return;
    }
    //是否需要告警
    if (!taskAlertSetting.isAlertEnabled()) {
      return;
    }
    StringList alertTypes = taskAlertSetting.getAlertTypes();
    Date nowDate = new Date();
    String alertMsg =
          "[点击查看](" + dpUrl + "#/flowmanage/executions?taskId=" + taskInfo.getTaskId() + ")";
    String taskStatus = execution.getStatus();
    Map<String, String> alertMap = this.taskManager.getTimeOutMap(execution.getTaskName());

    for (String alertType : alertTypes.getList()) {
      Map<Object, Object> map = new HashMap<>();
      map.put(MAP_KEY_TASK_NAME, taskInfo.getName());
      map.put(MAP_KEY_MSG, alertMsg);
      map.put(MAP_KEY_EXEC_ID, String.valueOf(execution.getExecId()));
      if (ALERT_FAILED.equals(alertType)) {
        if (!JobStatus.FAILED.name().equals(taskStatus)
              && !JobStatus.FAILED_FINISHING.name().equals(taskStatus)) {
          continue;
        }
        //此任务是结束状态，检验超时的map中有无此记录，有了删除此记录
        if (null != alertMap && !alertMap.isEmpty()) {
          this.taskManager.removeTimeOutMap(taskInfo.getName());
        }
        map.put(MAP_KEY_STATUS, ALERT_FAILED);
        sendMsg(TemplateUtil.TEM_ALERT_COMMON, taskInfo, ALERT_TITLE, map);
      } else if (ALERT_STARTED.equals(alertType)) {
        //发流程开始消息
        if (JobStatus.RUNNING.name().equals(taskStatus) || JobStatus.SUCCEEDED.name()
              .equals(taskStatus) ||
              JobStatus.FAILED.name().equals(taskStatus)) {
          String key = taskInfo.getName() + "#" + execution.getStartTime().getTime();
          if (null != startJobCache.get(key) || System.currentTimeMillis()
                - execution.getStartTime().getTime() > 120000) {
            continue;
          }
          SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          map.put(MAP_KEY_START_TIME, sdf.format(execution.getStartTime()));
          map.put(MAP_KEY_STATUS, ALERT_STARTED);
          sendMsg(TemplateUtil.TEM_START, taskInfo, ALERT_TITLE, map);
          startJobCache.set(key, START_JOB_CACHE_TTL);
        }

      } else if (ALERT_FINISHED.equals(alertType)) {
        if (!JobStatus.SUCCEEDED.name().equals(taskStatus)) {
          continue;
        }
        //此任务是结束状态，检验超时的map中有无此记录，有了删除此记录
        if (null != alertMap && !alertMap.isEmpty()) {
          this.taskManager.removeTimeOutMap(taskInfo.getName());
        }
        //发流程结束消息
        map.put(MAP_KEY_STATUS, ALERT_SUCCEEDED);
        sendMsg(TemplateUtil.TEM_ALERT_COMMON, taskInfo, ALERT_TITLE, map);
      } else if (ALERT_TIMEOUT.equals(alertType)) {
        //发超时消息
        if (!JobStatus.RUNNING.name().equals(taskStatus)
              && !JobStatus.PREPARING.name().equals(taskStatus)) {
          continue;
        }
        int alertDuration = taskAlertSetting.getTimeOutMinutes();
        int duration = 0;
        if (JobStatus.PREPARING.name().equals(taskStatus)) {
          duration = TimeUtil.minutesBetween(nowDate, execution.getCreateTime());
        } else {
          duration = TimeUtil.minutesBetween(nowDate, execution.getStartTime());
        }
        if (duration < alertDuration) {
          continue;
        }

        map.put(MAP_KEY_EXPECT_TIME, alertDuration);
        map.put(MAP_KEY_RUNNUNG_TIME, duration);
        map.put(MAP_KEY_STATUS, ALERT_TIMEOUT);

        //如果持续时长大于设定的值，发送告警信息
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        if (null == alertMap || alertMap.isEmpty()) {
          sendMsg(TemplateUtil.TEM_TIME_OUT, taskInfo, ALERT_TITLE, map);

          Map<String, String> maps = new HashMap<>();
          maps.put(MAP_KEY_TIME, sdf.format(nowDate));
          maps.put(MAP_KEY_FREQUENCY, "1");
          this.taskManager.putToTimeOutMap(execution.getTaskName(), maps);
        } else {
          boolean send = false;
          String lastTime = alertMap.get(MAP_KEY_TIME);
          String frequency = alertMap.get(MAP_KEY_FREQUENCY);//超时次数
          //5次之内，5分钟告警一次
          if (Integer.valueOf(frequency) <= 5) {
            Date lastDate = sdf.parse(lastTime);
            //5分钟报一次
            if (TimeUtil.minutesBetween(nowDate, lastDate) < 5) {
              continue;
            }
            send = true;
          } else {
            //5次之后，10分钟告警一次
            Date lastDate = sdf.parse(lastTime);
            //10分钟报一次
            if (TimeUtil.minutesBetween(nowDate, lastDate) < 10) {
              continue;
            }
            send = true;
          }
          if (send) {
            //发告警信息
            sendMsg(TemplateUtil.TEM_TIME_OUT, taskInfo, ALERT_TITLE, map);
            //将发过的信息加入timeoutAlertMap，记录值
            Map<String, String> maps = new HashMap<>();
            maps.put(MAP_KEY_TIME, sdf.format(nowDate));
            maps.put(MAP_KEY_FREQUENCY, Integer.valueOf(frequency) + 1 + "");
            this.taskManager.putToTimeOutMap(execution.getTaskName(), maps);
          }

        }
      }
    }
  }

  /**
   * sendmsg .
   */
  public void sendMsg(String template, TaskInfo taskInfo, String title, Map<Object, Object> map)
        throws Exception {

    GroupInfo groupInfo = groupInfoService.getGroupInfo(taskInfo.getOwnerId());
    List<GroupProfile> profileList = groupInfo.getProfiles();
    if (null == profileList || profileList.size() < 1) {
      logger.error("profileList is null.");
      return;
    }
    GroupProfile alertProfile = GroupInfo.getProfile(groupInfo.getProfiles(), "alert");
    if (null == alertProfile) {
      logger.error("alertProfile is null.");
      return;
    }
    if (!"true".equals(alertProfile.getSetting("enabled"))) {
      return;
    }
    TaskAlertSetting alertSetting = taskInfo.getAlertSetting();
    if (null == alertSetting) {
      return;
    }
    if (!alertSetting.isAlertEnabled()) {
      return;
    }

    Message message = new Message();
    message.setTitle(title);
    message.setTime(new Date());
    map.put(MAP_KEY_GROUP_NAME, groupInfo.getGroupName());
    message.setContent(TemplateUtil.genAlertMsgByTemplate(template, map));
    String dingMsg = DingDingMsgUtil.buildDingDingMsg(message);

    if (alertSetting.isSendDefaultDing()) {
      logger.error(JsonUtil.toJson(alertProfile));
      String dingUrl = alertProfile.getSetting("dingAddr");
      logger.error("dingding url is :" + dingUrl);
      new DingDingMsgUtil().sendDingDingMsg(dingUrl, dingMsg);
    }

    MailItem mailItem = new MailItem();
    //mailItem.setMailType("html");
    mailItem.setSubject(title);
    mailItem.setSendMsg(TemplateUtil.genAlertMsgByTemplate(template, map));
    if (alertSetting.isSendDefaultEmail()) {
      mailItem.setRecipient(alertProfile.getSetting("emailAddr"));
      MailSource source = MailSendUtil.buildMailSource();
      MailSendUtil.sendSimpleEmail(source, mailItem);
    }
    List<AlertInfo> alertInfos = alertSetting.getAlertTargets().getList();
    for (AlertInfo alertInfo : alertInfos) {
      send(alertInfo, dingMsg, mailItem);
    }
  }

  public boolean send(AlertInfo alertInfo, String msg, MailItem mailItem) {
    if (AlertType.DINGDING.equals(alertInfo.getAlertType())) {
      String dingUrls = alertInfo.getAlertAddr();
      String[] dingUrl = dingUrls.split(",");
      for (String ding : dingUrl) {
        try {
          new DingDingMsgUtil().sendDingDingMsg(ding, msg);
        } catch (IOException e) {
          logger.error("send ding msg error,ding addr:" + ding, e);
          return false;
        }
      }
    } else if (AlertType.EMAIL.equals(alertInfo.getAlertType())) {
      String dingUrls = alertInfo.getAlertAddr();
      String[] dingUrl = dingUrls.split(",");
      for (String ding : dingUrl) {
        mailItem.setRecipient(ding);
        MailSource source = MailSendUtil.buildMailSource();
        MailSendUtil.sendSimpleEmail(source, mailItem);
      }
    }
    return true;
  }
}
